import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;


// 此处暂时不考虑泛型，只保存 String 类型
class MyBlockingQueue{
    // 通过数组表示当前阻塞队列
    private String[] data;
    // [head, tail) 队列中有效元素的区间
    private int head = 0; // 队头
    private int tail = 0; // 队尾
    private int size = 0;
    // 手动定义锁对象. 使用 this 也可以
    private Object locker = new Object();

    public MyBlockingQueue(int capacity){
        if(capacity <= 0){
            throw new IllegalArgumentException("capacity must be positive.");
        }
        data = new String[capacity];
    }

    public void put(String elem) throws InterruptedException {
        // 保证写入操作的原子性
        synchronized (locker){
            // if(size == data.length) 写成if也可以，在当前代码没有错，但while普适性更高
            while(size == data.length){ //当等待被唤醒之后，先确认一下条件，然后再决定是继续执行还是再次等待
                // 队列已满，阻塞
                locker.wait();
            }
            // 新的元素放到 tail 所在位置
            data[tail] = elem;
            tail++;
            if(tail >= data.length){
                tail = 0; 
            }
            size++;
            locker.notify(); // 唤醒take的 wait
        }
    }

    public String take() throws InterruptedException {
        // 保证原子性
        synchronized (locker){
            while (size == 0){
                // 队列为空，阻塞
                locker.wait();
            }
            // 取出 head 位置的元素
            String ret = data[head];
            head++;
            if(head >= data.length){
                head = 0;
            }
            size--;
            locker.notify(); // 唤醒put的 wait
            return ret;
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        // 简单的生产者消费者模型的代码.
        // 使用一个线程表示生产者
        // 一个线程表示消费者.
        // 搞一个阻塞队列, 生产者往阻塞队列中放一个整数, 消费者进行获取.
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(1000);

        // 生产者
        Thread t1 = new Thread(() -> {
            int n = 0;
            while(true){
                try {
                    myBlockingQueue.put(n + "");
                    System.out.println("生产了" + n);
                    n++;
                    //Thread.sleep(1000); //消费快，生产慢
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 消费者
        Thread t2 = new Thread(() -> {
            while(true){
                try {
                    String n = myBlockingQueue.take();
                    System.out.println("消费了" + n);
                    Thread.sleep(1000); //生产快，消费慢
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }


    public static void main1(String[] args) {
        // 基于数组
        BlockingQueue<Long> blockingQueue = new ArrayBlockingQueue<>(100);
        //基于链表
        //BlockingQueue<String> blockingQueue2 = new LinkedBlockingQueue<>();
        //基于堆
        //BlockingQueue<String> blockingQueue3 = new PriorityBlockingQueue<>();

        Thread t1 = new Thread(() -> {
            long n = 0;
            while(true){
                try {
                    blockingQueue.put(n);
                    System.out.println("生产了" + n);
                    n++;
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread t2 = new Thread(() -> {
            while(true){
                try {
                    long n = blockingQueue.take();
                    System.out.println("消费了" + n);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}
